/*
 * MoveController.java
 *
 * Created on May 5, 2007, 8:40 PM
 *
 * Copyright (c) 2007 Daniel Gronau
 *
 * This file is part of KanjiTori.
 *
 * KanjiTori is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * KanjiTori is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
 *
 */

package kanjitori.gamestate.controller;

import com.jme.input.controls.GameControl;
import com.jme.math.Vector3f;
import com.jme.renderer.Camera;
import com.jme.scene.Controller;
import com.jme.scene.Node;
import com.jme.system.DisplaySystem;
import kanjitori.map.Content;
import kanjitori.map.ContentMatrix;
import kanjitori.map.Position;
import static kanjitori.map.Position.Direction.*;

/**
 * This class moves a node (normally the player node) forward, backward,
 * left strafe and right strafe in terms of the camera.
 * @author DGronau
 */
public class MoveController extends Controller {

    private static float SLIDE_DIST = 0.6f;
    private static float INV_SQRT2 = 0.707106781f;

    //temp holder for the multiplication of the direction and time
    private static final Vector3f tempVa = new Vector3f();
    //the camera to manipulate
    private Node node;
    private float angle;
    private float speed;
    private GameControl forward;
    private GameControl backward;
    private GameControl left;
    private GameControl right;

    private static ContentMatrix contentMatrix;

    public static void setContentMatrix(ContentMatrix contentMatrix) {
        MoveController.contentMatrix = contentMatrix;
    }

    /** Creates a new instance of MoveController
     * @param forward
     * @param backward
     * @param left
     * @param right
     * @param node
     * @param speed
     */
    public MoveController(GameControl forward, GameControl backward, GameControl left, GameControl right, Node node, float speed) {
        this.forward = forward;
        this.backward = backward;
        this.left = left;
        this.right = right;
        this.node = node;
        this.speed = speed;
    }

    public void update(float time) {
        float totalForward = forward.getValue() - backward.getValue();
        float totalLeft = left.getValue() - right.getValue();

        //no movement
        if (totalForward == 0.0f && totalLeft == 0.0f) {
            return;
        }

        //normalize diagonal movement - no cheating!
        if (totalForward * totalLeft != 0.0f) {
            totalForward *= INV_SQRT2;
            totalLeft *= INV_SQRT2;
        }

        Vector3f loc = node.getLocalTranslation();
        Camera camera = DisplaySystem.getDisplaySystem().getRenderer().getCamera();
        Vector3f dir = camera.getDirection().mult(totalForward).addLocal(camera.getLeft().mult(totalLeft));
        loc.addLocal(dir.mult(speed * time, tempVa));
        loc.y = 0;
        slide(loc);
        node.setLocalTranslation(loc);
    }

    private void slide(Vector3f loc) {
        float xpos = loc.x / 2;
        float ypos = loc.z / 2;
        Position pos = Position.get(Math.round(xpos), Math.round(ypos));

        float xd = xpos - pos.x();
        if (tooSmall(xd) && contentMatrix.getContent(pos.getNeighbor(LEFT)) == Content.SOLID) {
            loc.x = 2 * pos.x() - SLIDE_DIST;
        } else if (tooBig(xd) && contentMatrix.getContent(pos.getNeighbor(RIGHT)) == Content.SOLID) {
            loc.x = 2 * pos.x() + SLIDE_DIST;
        }

        float yd = ypos - pos.y();
        if (tooSmall(yd) && contentMatrix.getContent(pos.getNeighbor(LOWER)) == Content.SOLID) {
            loc.z = 2 * pos.y() - SLIDE_DIST;
        } else if (tooBig(yd) && contentMatrix.getContent(pos.getNeighbor(UPPER)) == Content.SOLID) {
            loc.z = 2 * pos.y() + SLIDE_DIST;
        }

        //the "corner" case
        xd = loc.x / 2 - pos.x();
        yd = loc.z / 2 - pos.y();

        if (tooSmall(xd) && tooSmall(yd) && contentMatrix.getContent(pos.getNeighbor(LOWER_LEFT)) == Content.SOLID) {
            if (xd > yd) {
                loc.x = 2 * pos.x() - SLIDE_DIST;
            } else {
                loc.z = 2 * pos.y() - SLIDE_DIST;
            }
        } else if (tooBig(xd) && tooSmall(yd) && contentMatrix.getContent(pos.getNeighbor(LOWER_RIGHT)) == Content.SOLID) {
            if (-xd > yd) {
                loc.x = 2 * pos.x() + SLIDE_DIST;
            } else {
                loc.z = 2 * pos.y() - SLIDE_DIST;
            }
        } else if (tooSmall(xd) && tooBig(SLIDE_DIST) && contentMatrix.getContent(pos.getNeighbor(UPPER_LEFT)) == Content.SOLID) {
            if (xd > -yd) {
                loc.x = 2 * pos.x() - SLIDE_DIST;
            } else {
                loc.z = 2 * pos.y() + SLIDE_DIST;
            }
        } else if (tooBig(xd) && tooBig(yd) && contentMatrix.getContent(pos.getNeighbor(UPPER_RIGHT)) == Content.SOLID) {
            if (xd < yd) {
                loc.x = 2 * pos.x() + SLIDE_DIST;
            } else {
                loc.z = 2 * pos.y() + SLIDE_DIST;
            }
        }
    }
    
    private static boolean tooSmall(float f) {
        return f < -SLIDE_DIST / 2;
    }
    
    private static boolean tooBig(float f) {
        return f > SLIDE_DIST / 2;
    }
}
